--- /dev/null
+/*
+ * Copyright (c) The mldsa-native project authors
+ * SPDX-License-Identifier: Apache-2.0 OR ISC OR MIT
+ */
+/* Based on the public domain implementation in crypto_hash/keccakc512/simple/
+ * from http://bench.cr.yp.to/supercop.html by Ronny Van Keer and the public
+ * domain "TweetFips202" implementation from https://twitter.com/tweetfips202 by
+ * Gilles Van Assche, Daniel J. Bernstein, and Peter Schwabe */
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "shake.h"
+#include "utils.h"
+
+#define NROUNDS 24
+#define ROL(a, offset) ((a << offset) ^ (a >> (64 - offset)))
+
+#define MLD_KECCAK_NROUNDS 24
+#define MLD_KECCAK_ROL(a, offset) ((a << offset) ^ (a >> (64 - offset)))
+
+static void
+mld_keccakf1600_extract_bytes(uint64_t *state, unsigned char *data,
+ unsigned offset, unsigned length)
+{
+ unsigned i;
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+ uint8_t *state_ptr = (uint8_t *)state + offset;
+ for (i = 0; i < length; i++)
+ {
+ data[i] = state_ptr[i];
+ }
+#else
+ /* Portable version */
+ for (i = 0; i < length; i++)
+ {
+ data[i] = (state[(offset + i) >> 3] >> (8 * ((offset + i) & 0x07))) & 0xFF;
+ }
+#endif
+}
+
+static void
+mld_keccakf1600_xor_bytes(uint64_t *state, const unsigned char *data,
+ unsigned offset, unsigned length)
+{
+ unsigned i;
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+ uint8_t *state_ptr = (uint8_t *)state + offset;
+ for (i = 0; i < length; i++)
+ {
+ state_ptr[i] ^= data[i];
+ }
+#else
+ /* Portable version */
+ for (i = 0; i < length; i++)
+ {
+ state[(offset + i) >> 3] ^= (uint64_t)data[i]
+ << (8 * ((offset + i) & 0x07));
+ }
+#endif
+}
+
+
+static const uint64_t mld_KeccakF_RoundConstants[MLD_KECCAK_NROUNDS] = {
+ (uint64_t)0x0000000000000001ULL, (uint64_t)0x0000000000008082ULL,
+ (uint64_t)0x800000000000808aULL, (uint64_t)0x8000000080008000ULL,
+ (uint64_t)0x000000000000808bULL, (uint64_t)0x0000000080000001ULL,
+ (uint64_t)0x8000000080008081ULL, (uint64_t)0x8000000000008009ULL,
+ (uint64_t)0x000000000000008aULL, (uint64_t)0x0000000000000088ULL,
+ (uint64_t)0x0000000080008009ULL, (uint64_t)0x000000008000000aULL,
+ (uint64_t)0x000000008000808bULL, (uint64_t)0x800000000000008bULL,
+ (uint64_t)0x8000000000008089ULL, (uint64_t)0x8000000000008003ULL,
+ (uint64_t)0x8000000000008002ULL, (uint64_t)0x8000000000000080ULL,
+ (uint64_t)0x000000000000800aULL, (uint64_t)0x800000008000000aULL,
+ (uint64_t)0x8000000080008081ULL, (uint64_t)0x8000000000008080ULL,
+ (uint64_t)0x0000000080000001ULL, (uint64_t)0x8000000080008008ULL};
+
+static void
+mld_keccakf1600_permute(uint64_t *state)
+{
+ unsigned round;
+
+ uint64_t Aba, Abe, Abi, Abo, Abu;
+ uint64_t Aga, Age, Agi, Ago, Agu;
+ uint64_t Aka, Ake, Aki, Ako, Aku;
+ uint64_t Ama, Ame, Ami, Amo, Amu;
+ uint64_t Asa, Ase, Asi, Aso, Asu;
+ uint64_t BCa, BCe, BCi, BCo, BCu;
+ uint64_t Da, De, Di, Do, Du;
+ uint64_t Eba, Ebe, Ebi, Ebo, Ebu;
+ uint64_t Ega, Ege, Egi, Ego, Egu;
+ uint64_t Eka, Eke, Eki, Eko, Eku;
+ uint64_t Ema, Eme, Emi, Emo, Emu;
+ uint64_t Esa, Ese, Esi, Eso, Esu;
+
+ /* copyFromState(A, state) */
+ Aba = state[0];
+ Abe = state[1];
+ Abi = state[2];
+ Abo = state[3];
+ Abu = state[4];
+ Aga = state[5];
+ Age = state[6];
+ Agi = state[7];
+ Ago = state[8];
+ Agu = state[9];
+ Aka = state[10];
+ Ake = state[11];
+ Aki = state[12];
+ Ako = state[13];
+ Aku = state[14];
+ Ama = state[15];
+ Ame = state[16];
+ Ami = state[17];
+ Amo = state[18];
+ Amu = state[19];
+ Asa = state[20];
+ Ase = state[21];
+ Asi = state[22];
+ Aso = state[23];
+ Asu = state[24];
+
+ for (round = 0; round < MLD_KECCAK_NROUNDS; round += 2)
+ {
+ /* prepareTheta */
+ BCa = Aba ^ Aga ^ Aka ^ Ama ^ Asa;
+ BCe = Abe ^ Age ^ Ake ^ Ame ^ Ase;
+ BCi = Abi ^ Agi ^ Aki ^ Ami ^ Asi;
+ BCo = Abo ^ Ago ^ Ako ^ Amo ^ Aso;
+ BCu = Abu ^ Agu ^ Aku ^ Amu ^ Asu;
+
+ /* thetaRhoPiChiIotaPrepareTheta(round, A, E) */
+ Da = BCu ^ MLD_KECCAK_ROL(BCe, 1);
+ De = BCa ^ MLD_KECCAK_ROL(BCi, 1);
+ Di = BCe ^ MLD_KECCAK_ROL(BCo, 1);
+ Do = BCi ^ MLD_KECCAK_ROL(BCu, 1);
+ Du = BCo ^ MLD_KECCAK_ROL(BCa, 1);
+
+ Aba ^= Da;
+ BCa = Aba;
+ Age ^= De;
+ BCe = MLD_KECCAK_ROL(Age, 44);
+ Aki ^= Di;
+ BCi = MLD_KECCAK_ROL(Aki, 43);
+ Amo ^= Do;
+ BCo = MLD_KECCAK_ROL(Amo, 21);
+ Asu ^= Du;
+ BCu = MLD_KECCAK_ROL(Asu, 14);
+ Eba = BCa ^ ((~BCe) & BCi);
+ Eba ^= (uint64_t)mld_KeccakF_RoundConstants[round];
+ Ebe = BCe ^ ((~BCi) & BCo);
+ Ebi = BCi ^ ((~BCo) & BCu);
+ Ebo = BCo ^ ((~BCu) & BCa);
+ Ebu = BCu ^ ((~BCa) & BCe);
+
+ Abo ^= Do;
+ BCa = MLD_KECCAK_ROL(Abo, 28);
+ Agu ^= Du;
+ BCe = MLD_KECCAK_ROL(Agu, 20);
+ Aka ^= Da;
+ BCi = MLD_KECCAK_ROL(Aka, 3);
+ Ame ^= De;
+ BCo = MLD_KECCAK_ROL(Ame, 45);
+ Asi ^= Di;
+ BCu = MLD_KECCAK_ROL(Asi, 61);
+ Ega = BCa ^ ((~BCe) & BCi);
+ Ege = BCe ^ ((~BCi) & BCo);
+ Egi = BCi ^ ((~BCo) & BCu);
+ Ego = BCo ^ ((~BCu) & BCa);
+ Egu = BCu ^ ((~BCa) & BCe);
+
+ Abe ^= De;
+ BCa = MLD_KECCAK_ROL(Abe, 1);
+ Agi ^= Di;
+ BCe = MLD_KECCAK_ROL(Agi, 6);
+ Ako ^= Do;
+ BCi = MLD_KECCAK_ROL(Ako, 25);
+ Amu ^= Du;
+ BCo = MLD_KECCAK_ROL(Amu, 8);
+ Asa ^= Da;
+ BCu = MLD_KECCAK_ROL(Asa, 18);
+ Eka = BCa ^ ((~BCe) & BCi);
+ Eke = BCe ^ ((~BCi) & BCo);
+ Eki = BCi ^ ((~BCo) & BCu);
+ Eko = BCo ^ ((~BCu) & BCa);
+ Eku = BCu ^ ((~BCa) & BCe);
+
+ Abu ^= Du;
+ BCa = MLD_KECCAK_ROL(Abu, 27);
+ Aga ^= Da;
+ BCe = MLD_KECCAK_ROL(Aga, 36);
+ Ake ^= De;
+ BCi = MLD_KECCAK_ROL(Ake, 10);
+ Ami ^= Di;
+ BCo = MLD_KECCAK_ROL(Ami, 15);
+ Aso ^= Do;
+ BCu = MLD_KECCAK_ROL(Aso, 56);
+ Ema = BCa ^ ((~BCe) & BCi);
+ Eme = BCe ^ ((~BCi) & BCo);
+ Emi = BCi ^ ((~BCo) & BCu);
+ Emo = BCo ^ ((~BCu) & BCa);
+ Emu = BCu ^ ((~BCa) & BCe);
+
+ Abi ^= Di;
+ BCa = MLD_KECCAK_ROL(Abi, 62);
+ Ago ^= Do;
+ BCe = MLD_KECCAK_ROL(Ago, 55);
+ Aku ^= Du;
+ BCi = MLD_KECCAK_ROL(Aku, 39);
+ Ama ^= Da;
+ BCo = MLD_KECCAK_ROL(Ama, 41);
+ Ase ^= De;
+ BCu = MLD_KECCAK_ROL(Ase, 2);
+ Esa = BCa ^ ((~BCe) & BCi);
+ Ese = BCe ^ ((~BCi) & BCo);
+ Esi = BCi ^ ((~BCo) & BCu);
+ Eso = BCo ^ ((~BCu) & BCa);
+ Esu = BCu ^ ((~BCa) & BCe);
+
+ /* prepareTheta */
+ BCa = Eba ^ Ega ^ Eka ^ Ema ^ Esa;
+ BCe = Ebe ^ Ege ^ Eke ^ Eme ^ Ese;
+ BCi = Ebi ^ Egi ^ Eki ^ Emi ^ Esi;
+ BCo = Ebo ^ Ego ^ Eko ^ Emo ^ Eso;
+ BCu = Ebu ^ Egu ^ Eku ^ Emu ^ Esu;
+
+ /* thetaRhoPiChiIotaPrepareTheta(round+1, E, A) */
+ Da = BCu ^ MLD_KECCAK_ROL(BCe, 1);
+ De = BCa ^ MLD_KECCAK_ROL(BCi, 1);
+ Di = BCe ^ MLD_KECCAK_ROL(BCo, 1);
+ Do = BCi ^ MLD_KECCAK_ROL(BCu, 1);
+ Du = BCo ^ MLD_KECCAK_ROL(BCa, 1);
+
+ Eba ^= Da;
+ BCa = Eba;
+ Ege ^= De;
+ BCe = MLD_KECCAK_ROL(Ege, 44);
+ Eki ^= Di;
+ BCi = MLD_KECCAK_ROL(Eki, 43);
+ Emo ^= Do;
+ BCo = MLD_KECCAK_ROL(Emo, 21);
+ Esu ^= Du;
+ BCu = MLD_KECCAK_ROL(Esu, 14);
+ Aba = BCa ^ ((~BCe) & BCi);
+ Aba ^= (uint64_t)mld_KeccakF_RoundConstants[round + 1];
+ Abe = BCe ^ ((~BCi) & BCo);
+ Abi = BCi ^ ((~BCo) & BCu);
+ Abo = BCo ^ ((~BCu) & BCa);
+ Abu = BCu ^ ((~BCa) & BCe);
+
+ Ebo ^= Do;
+ BCa = MLD_KECCAK_ROL(Ebo, 28);
+ Egu ^= Du;
+ BCe = MLD_KECCAK_ROL(Egu, 20);
+ Eka ^= Da;
+ BCi = MLD_KECCAK_ROL(Eka, 3);
+ Eme ^= De;
+ BCo = MLD_KECCAK_ROL(Eme, 45);
+ Esi ^= Di;
+ BCu = MLD_KECCAK_ROL(Esi, 61);
+ Aga = BCa ^ ((~BCe) & BCi);
+ Age = BCe ^ ((~BCi) & BCo);
+ Agi = BCi ^ ((~BCo) & BCu);
+ Ago = BCo ^ ((~BCu) & BCa);
+ Agu = BCu ^ ((~BCa) & BCe);
+
+ Ebe ^= De;
+ BCa = MLD_KECCAK_ROL(Ebe, 1);
+ Egi ^= Di;
+ BCe = MLD_KECCAK_ROL(Egi, 6);
+ Eko ^= Do;
+ BCi = MLD_KECCAK_ROL(Eko, 25);
+ Emu ^= Du;
+ BCo = MLD_KECCAK_ROL(Emu, 8);
+ Esa ^= Da;
+ BCu = MLD_KECCAK_ROL(Esa, 18);
+ Aka = BCa ^ ((~BCe) & BCi);
+ Ake = BCe ^ ((~BCi) & BCo);
+ Aki = BCi ^ ((~BCo) & BCu);
+ Ako = BCo ^ ((~BCu) & BCa);
+ Aku = BCu ^ ((~BCa) & BCe);
+
+ Ebu ^= Du;
+ BCa = MLD_KECCAK_ROL(Ebu, 27);
+ Ega ^= Da;
+ BCe = MLD_KECCAK_ROL(Ega, 36);
+ Eke ^= De;
+ BCi = MLD_KECCAK_ROL(Eke, 10);
+ Emi ^= Di;
+ BCo = MLD_KECCAK_ROL(Emi, 15);
+ Eso ^= Do;
+ BCu = MLD_KECCAK_ROL(Eso, 56);
+ Ama = BCa ^ ((~BCe) & BCi);
+ Ame = BCe ^ ((~BCi) & BCo);
+ Ami = BCi ^ ((~BCo) & BCu);
+ Amo = BCo ^ ((~BCu) & BCa);
+ Amu = BCu ^ ((~BCa) & BCe);
+
+ Ebi ^= Di;
+ BCa = MLD_KECCAK_ROL(Ebi, 62);
+ Ego ^= Do;
+ BCe = MLD_KECCAK_ROL(Ego, 55);
+ Eku ^= Du;
+ BCi = MLD_KECCAK_ROL(Eku, 39);
+ Ema ^= Da;
+ BCo = MLD_KECCAK_ROL(Ema, 41);
+ Ese ^= De;
+ BCu = MLD_KECCAK_ROL(Ese, 2);
+ Asa = BCa ^ ((~BCe) & BCi);
+ Ase = BCe ^ ((~BCi) & BCo);
+ Asi = BCi ^ ((~BCo) & BCu);
+ Aso = BCo ^ ((~BCu) & BCa);
+ Asu = BCu ^ ((~BCa) & BCe);
+ }
+
+ /* copyToState(state, A) */
+ state[0] = Aba;
+ state[1] = Abe;
+ state[2] = Abi;
+ state[3] = Abo;
+ state[4] = Abu;
+ state[5] = Aga;
+ state[6] = Age;
+ state[7] = Agi;
+ state[8] = Ago;
+ state[9] = Agu;
+ state[10] = Aka;
+ state[11] = Ake;
+ state[12] = Aki;
+ state[13] = Ako;
+ state[14] = Aku;
+ state[15] = Ama;
+ state[16] = Ame;
+ state[17] = Ami;
+ state[18] = Amo;
+ state[19] = Amu;
+ state[20] = Asa;
+ state[21] = Ase;
+ state[22] = Asi;
+ state[23] = Aso;
+ state[24] = Asu;
+}
+
+static void
+mld_keccakf1600x4_extract_bytes(uint64_t *state, unsigned char *data0,
+ unsigned char *data1, unsigned char *data2,
+ unsigned char *data3, unsigned offset,
+ unsigned length)
+{
+ mld_keccakf1600_extract_bytes(state + MLD_KECCAK_LANES * 0, data0, offset,
+ length);
+ mld_keccakf1600_extract_bytes(state + MLD_KECCAK_LANES * 1, data1, offset,
+ length);
+ mld_keccakf1600_extract_bytes(state + MLD_KECCAK_LANES * 2, data2, offset,
+ length);
+ mld_keccakf1600_extract_bytes(state + MLD_KECCAK_LANES * 3, data3, offset,
+ length);
+}
+
+static void
+mld_keccakf1600x4_xor_bytes(uint64_t *state, const unsigned char *data0,
+ const unsigned char *data1,
+ const unsigned char *data2,
+ const unsigned char *data3, unsigned offset,
+ unsigned length)
+{
+ mld_keccakf1600_xor_bytes(state + MLD_KECCAK_LANES * 0, data0, offset,
+ length);
+ mld_keccakf1600_xor_bytes(state + MLD_KECCAK_LANES * 1, data1, offset,
+ length);
+ mld_keccakf1600_xor_bytes(state + MLD_KECCAK_LANES * 2, data2, offset,
+ length);
+ mld_keccakf1600_xor_bytes(state + MLD_KECCAK_LANES * 3, data3, offset,
+ length);
+}
+
+static void
+mld_keccakf1600x4_permute(uint64_t *state)
+{
+ mld_keccakf1600_permute(state + MLD_KECCAK_LANES * 0);
+ mld_keccakf1600_permute(state + MLD_KECCAK_LANES * 1);
+ mld_keccakf1600_permute(state + MLD_KECCAK_LANES * 2);
+ mld_keccakf1600_permute(state + MLD_KECCAK_LANES * 3);
+}
+
+/*************************************************
+ * Name: load64
+ *
+ * Description: Load 8 bytes into uint64_t in little-endian order
+ *
+ * Arguments: - const uint8_t *x: pointer to input byte array
+ *
+ * Returns the loaded 64-bit unsigned integer
+ **************************************************/
+static uint64_t load64(const uint8_t x[8])
+{
+ unsigned int i;
+ uint64_t r = 0;
+
+ for (i = 0; i < 8; i++)
+ {
+ r |= (uint64_t)x[i] << 8 * i;
+ }
+
+ return r;
+}
+
+/*************************************************
+ * Name: store64
+ *
+ * Description: Store a 64-bit integer to array of 8 bytes in little-endian
+ *order
+ *
+ * Arguments: - uint8_t *x: pointer to the output byte array (allocated)
+ * - uint64_t u: input 64-bit unsigned integer
+ **************************************************/
+static void store64(uint8_t x[8], uint64_t u)
+{
+ unsigned int i;
+
+ for (i = 0; i < 8; i++)
+ {
+ /* Explicitly truncate to uint8_t */
+ x[i] = (uint8_t)((u >> (8 * i)) & 0xFF);
+ }
+}
+
+/*************************************************
+ * Name: keccak_init
+ *
+ * Description: Initializes the Keccak state.
+ *
+ * Arguments: - uint64_t *s: pointer to Keccak state
+ **************************************************/
+static void keccak_init(uint64_t s[MLD_KECCAK_LANES])
+{
+ unsigned int i;
+ for (i = 0; i < MLD_KECCAK_LANES; i++)
+ {
+ s[i] = 0;
+ }
+}
+
+/*************************************************
+ * Name: keccak_absorb
+ *
+ * Description: Absorb step of Keccak; incremental.
+ *
+ * Arguments: - uint64_t *s: pointer to Keccak state
+ * - unsigned int pos: position in current block to be absorbed
+ * - unsigned int r: rate in bytes (e.g., 168 for SHAKE128)
+ * - const uint8_t *in: pointer to input to be absorbed into s
+ * - size_t inlen: length of input in bytes
+ *
+ * Returns new position pos in current block
+ **************************************************/
+static unsigned int keccak_absorb(uint64_t s[MLD_KECCAK_LANES],
+ unsigned int pos, unsigned int r,
+ const uint8_t *in, size_t inlen)
+{
+ unsigned int i;
+
+ while (pos + inlen >= r)
+ {
+ for (i = pos; i < r; i++)
+ {
+ s[i / 8] ^= (uint64_t)*in++ << 8 * (i % 8);
+ }
+ inlen -= r - pos;
+ mld_keccakf1600_permute(s);
+ pos = 0;
+ }
+
+ for (i = pos; i < pos + inlen; i++)
+ {
+ s[i / 8] ^= (uint64_t)*in++ << 8 * (i % 8);
+ }
+
+ return i;
+}
+
+/*************************************************
+ * Name: keccak_finalize
+ *
+ * Description: Finalize absorb step.
+ *
+ * Arguments: - uint64_t *s: pointer to Keccak state
+ * - unsigned int pos: position in current block to be absorbed
+ * - unsigned int r: rate in bytes (e.g., 168 for SHAKE128)
+ * - uint8_t p: domain separation byte
+ **************************************************/
+static void keccak_finalize(uint64_t s[MLD_KECCAK_LANES], unsigned int pos,
+ unsigned int r, uint8_t p)
+{
+ s[pos / 8] ^= (uint64_t)p << 8 * (pos % 8);
+ s[r / 8 - 1] ^= 1ULL << 63;
+}
+
+/*************************************************
+ * Name: keccak_squeeze
+ *
+ * Description: Squeeze step of Keccak. Squeezes arbitratrily many bytes.
+ * Modifies the state. Can be called multiple times to keep
+ * squeezing, i.e., is incremental.
+ *
+ * Arguments: - uint8_t *out: pointer to output data
+ * - size_t outlen: number of bytes to be squeezed (written to out)
+ * - uint64_t *s: pointer to input/output Keccak state
+ * - unsigned int pos: number of bytes in current block already
+ *squeezed
+ * - unsigned int r: rate in bytes (e.g., 168 for SHAKE128)
+ *
+ * Returns new position pos in current block
+ **************************************************/
+static unsigned int keccak_squeeze(uint8_t *out, size_t outlen,
+ uint64_t s[MLD_KECCAK_LANES],
+ unsigned int pos, unsigned int r)
+{
+ unsigned int i;
+ size_t out_offset = 0;
+
+ /* Reference: This code is re-factored from the reference implementation
+ * to facilitate proof with CBMC and to improve readability.
+ *
+ * Take a mutable copy of outlen to count down the number of bytes
+ * still to squeeze. The initial value of outlen is needed for the CBMC
+ * assigns() clauses. */
+ size_t bytes_to_go = outlen;
+
+ while (bytes_to_go > 0)
+ {
+ if (pos == r)
+ {
+ mld_keccakf1600_permute(s);
+ pos = 0;
+ }
+ for (i = pos; i < r && i < pos + bytes_to_go; i++)
+ {
+ const uint64_t lane = s[i / 8];
+ out[out_offset] = (uint8_t)((lane >> (8 * (i % 8))) & 0xFF);
+ out_offset++;
+ }
+ bytes_to_go -= i - pos;
+ pos = i;
+ }
+
+ return pos;
+}
+
+/*************************************************
+ * Name: keccak_absorb_once
+ *
+ * Description: Absorb step of Keccak;
+ * non-incremental, starts by zeroeing the state.
+ *
+ * Arguments: - uint64_t *s: pointer to (uninitialized) output Keccak state
+ * - const unsigned int r: rate in bytes (e.g., 168 for SHAKE128)
+ * - const uint8_t *in: pointer to input to be absorbed into s
+ * - size_t inlen: length of input in bytes
+ * - uint8_t p: domain-separation byte for different Keccak-derived
+ *functions
+ **************************************************/
+static void keccak_absorb_once(uint64_t s[MLD_KECCAK_LANES],
+ const unsigned int r, const uint8_t *in,
+ size_t inlen, uint8_t p)
+{
+ unsigned int i;
+
+ for (i = 0; i < MLD_KECCAK_LANES; i++)
+ {
+ s[i] = 0;
+ }
+
+ while (inlen >= r)
+ {
+ for (i = 0; i < r / 8; i++)
+ {
+ s[i] ^= load64(in + 8 * i);
+ }
+ in += r;
+ inlen -= r;
+ mld_keccakf1600_permute(s);
+ }
+
+ for (i = 0; i < inlen; i++)
+ {
+ s[i / 8] ^= (uint64_t)in[i] << 8 * (i % 8);
+ }
+
+ s[i / 8] ^= (uint64_t)p << 8 * (i % 8);
+ s[(r - 1) / 8] ^= 1ULL << 63;
+}
+
+/*************************************************
+ * Name: keccak_squeezeblocks
+ *
+ * Description: Squeeze step of Keccak. Squeezes full blocks of r bytes each.
+ * Modifies the state. Can be called multiple times to keep
+ * squeezing, i.e., is incremental. Assumes zero bytes of current
+ * block have already been squeezed.
+ *
+ * Arguments: - uint8_t *out: pointer to output blocks
+ * - size_t nblocks: number of blocks to be squeezed (written to
+ *out)
+ * - uint64_t *s: pointer to input/output Keccak state
+ * - unsigned int r: rate in bytes (e.g., 168 for SHAKE128)
+ **************************************************/
+static void keccak_squeezeblocks(uint8_t *out, size_t nblocks,
+ uint64_t s[MLD_KECCAK_LANES], unsigned int r)
+{
+ while (nblocks > 0)
+ {
+ mld_keccakf1600_permute(s);
+ mld_keccakf1600_extract_bytes(s, out, 0, r);
+ out += r;
+ nblocks -= 1;
+ }
+}
+
+/*************************************************
+ * Name: shake128_init
+ *
+ * Description: Initilizes Keccak state for use as SHAKE128 XOF
+ *
+ * Arguments: - keccak_state *state: pointer to (uninitialized) Keccak state
+ **************************************************/
+void shake128_init(keccak_state *state)
+{
+ keccak_init(state->s);
+ state->pos = 0;
+}
+
+/*************************************************
+ * Name: shake128_absorb
+ *
+ * Description: Absorb step of the SHAKE128 XOF; incremental.
+ *
+ * Arguments: - keccak_state *state: pointer to (initialized) output Keccak
+ *state
+ * - const uint8_t *in: pointer to input to be absorbed into s
+ * - size_t inlen: length of input in bytes
+ **************************************************/
+void shake128_absorb(keccak_state *state, const uint8_t *in, size_t inlen)
+{
+ state->pos = keccak_absorb(state->s, state->pos, SHAKE128_RATE, in, inlen);
+}
+
+/*************************************************
+ * Name: shake128_finalize
+ *
+ * Description: Finalize absorb step of the SHAKE128 XOF.
+ *
+ * Arguments: - keccak_state *state: pointer to Keccak state
+ **************************************************/
+void shake128_finalize(keccak_state *state)
+{
+ keccak_finalize(state->s, state->pos, SHAKE128_RATE, 0x1F);
+ state->pos = SHAKE128_RATE;
+}
+
+/*************************************************
+ * Name: shake128_squeeze
+ *
+ * Description: Squeeze step of SHAKE128 XOF. Squeezes arbitraily many
+ * bytes. Can be called multiple times to keep squeezing.
+ *
+ * Arguments: - uint8_t *out: pointer to output blocks
+ * - size_t outlen : number of bytes to be squeezed (written to
+ *output)
+ * - keccak_state *s: pointer to input/output Keccak state
+ **************************************************/
+void shake128_squeeze(uint8_t *out, size_t outlen, keccak_state *state)
+{
+ state->pos = keccak_squeeze(out, outlen, state->s, state->pos, SHAKE128_RATE);
+}
+
+/*************************************************
+ * Name: shake128_absorb_once
+ *
+ * Description: Initialize, absorb into and finalize SHAKE128 XOF;
+ *non-incremental.
+ *
+ * Arguments: - keccak_state *state: pointer to (uninitialized) output Keccak
+ *state
+ * - const uint8_t *in: pointer to input to be absorbed into s
+ * - size_t inlen: length of input in bytes
+ **************************************************/
+void shake128_absorb_once(keccak_state *state, const uint8_t *in, size_t inlen)
+{
+ keccak_absorb_once(state->s, SHAKE128_RATE, in, inlen, 0x1F);
+ state->pos = SHAKE128_RATE;
+}
+
+/*************************************************
+ * Name: shake128_squeezeblocks
+ *
+ * Description: Squeeze step of SHAKE128 XOF. Squeezes full blocks of
+ * SHAKE128_RATE bytes each. Can be called multiple times
+ * to keep squeezing. Assumes new block has not yet been
+ * started (state->pos = SHAKE128_RATE).
+ *
+ * Arguments: - uint8_t *out: pointer to output blocks
+ * - size_t nblocks: number of blocks to be squeezed (written to
+ *output)
+ * - keccak_state *s: pointer to input/output Keccak state
+ **************************************************/
+void shake128_squeezeblocks(uint8_t *out, size_t nblocks, keccak_state *state)
+{
+ keccak_squeezeblocks(out, nblocks, state->s, SHAKE128_RATE);
+}
+
+void shake128_release(keccak_state *state)
+{
+ (void)state;
+ /* TODO: add secure zeroization*/
+}
+
+/*************************************************
+ * Name: shake256_init
+ *
+ * Description: Initilizes Keccak state for use as SHAKE256 XOF
+ *
+ * Arguments: - keccak_state *state: pointer to (uninitialized) Keccak state
+ **************************************************/
+void shake256_init(keccak_state *state)
+{
+ keccak_init(state->s);
+ state->pos = 0;
+}
+
+/*************************************************
+ * Name: shake256_absorb
+ *
+ * Description: Absorb step of the SHAKE256 XOF; incremental.
+ *
+ * Arguments: - keccak_state *state: pointer to (initialized) output Keccak
+ *state
+ * - const uint8_t *in: pointer to input to be absorbed into s
+ * - size_t inlen: length of input in bytes
+ **************************************************/
+void shake256_absorb(keccak_state *state, const uint8_t *in, size_t inlen)
+{
+ state->pos = keccak_absorb(state->s, state->pos, SHAKE256_RATE, in, inlen);
+}
+
+/*************************************************
+ * Name: shake256_finalize
+ *
+ * Description: Finalize absorb step of the SHAKE256 XOF.
+ *
+ * Arguments: - keccak_state *state: pointer to Keccak state
+ **************************************************/
+void shake256_finalize(keccak_state *state)
+{
+ keccak_finalize(state->s, state->pos, SHAKE256_RATE, 0x1F);
+ state->pos = SHAKE256_RATE;
+}
+
+/*************************************************
+ * Name: shake256_squeeze
+ *
+ * Description: Squeeze step of SHAKE256 XOF. Squeezes arbitraily many
+ * bytes. Can be called multiple times to keep squeezing.
+ *
+ * Arguments: - uint8_t *out: pointer to output blocks
+ * - size_t outlen : number of bytes to be squeezed (written to
+ *output)
+ * - keccak_state *s: pointer to input/output Keccak state
+ **************************************************/
+void shake256_squeeze(uint8_t *out, size_t outlen, keccak_state *state)
+{
+ state->pos = keccak_squeeze(out, outlen, state->s, state->pos, SHAKE256_RATE);
+}
+
+/*************************************************
+ * Name: shake256_absorb_once
+ *
+ * Description: Initialize, absorb into and finalize SHAKE256 XOF;
+ *non-incremental.
+ *
+ * Arguments: - keccak_state *state: pointer to (uninitialized) output Keccak
+ *state
+ * - const uint8_t *in: pointer to input to be absorbed into s
+ * - size_t inlen: length of input in bytes
+ **************************************************/
+void shake256_absorb_once(keccak_state *state, const uint8_t *in, size_t inlen)
+{
+ keccak_absorb_once(state->s, SHAKE256_RATE, in, inlen, 0x1F);
+ state->pos = SHAKE256_RATE;
+}
+
+/*************************************************
+ * Name: shake256_squeezeblocks
+ *
+ * Description: Squeeze step of SHAKE256 XOF. Squeezes full blocks of
+ * SHAKE256_RATE bytes each. Can be called multiple times
+ * to keep squeezing. Assumes next block has not yet been
+ * started (state->pos = SHAKE256_RATE).
+ *
+ * Arguments: - uint8_t *out: pointer to output blocks
+ * - size_t nblocks: number of blocks to be squeezed (written to
+ *output)
+ * - keccak_state *s: pointer to input/output Keccak state
+ **************************************************/
+void shake256_squeezeblocks(uint8_t *out, size_t nblocks, keccak_state *state)
+{
+ keccak_squeezeblocks(out, nblocks, state->s, SHAKE256_RATE);
+}
+
+void shake256_release(keccak_state *state)
+{
+ (void)state;
+ /* TODO: add secure zeroization*/
+}
+
+/*************************************************
+ * Name: shake128
+ *
+ * Description: SHAKE128 XOF with non-incremental API
+ *
+ * Arguments: - uint8_t *out: pointer to output
+ * - size_t outlen: requested output length in bytes
+ * - const uint8_t *in: pointer to input
+ * - size_t inlen: length of input in bytes
+ **************************************************/
+void shake128(uint8_t *out, size_t outlen, const uint8_t *in, size_t inlen)
+{
+ size_t nblocks;
+ keccak_state state;
+
+ shake128_absorb_once(&state, in, inlen);
+ nblocks = outlen / SHAKE128_RATE;
+ shake128_squeezeblocks(out, nblocks, &state);
+ outlen -= nblocks * SHAKE128_RATE;
+ out += nblocks * SHAKE128_RATE;
+ shake128_squeeze(out, outlen, &state);
+}
+
+/*************************************************
+ * Name: shake256
+ *
+ * Description: SHAKE256 XOF with non-incremental API
+ *
+ * Arguments: - uint8_t *out: pointer to output
+ * - size_t outlen: requested output length in bytes
+ * - const uint8_t *in: pointer to input
+ * - size_t inlen: length of input in bytes
+ **************************************************/
+void shake256(uint8_t *out, size_t outlen, const uint8_t *in, size_t inlen)
+{
+ size_t nblocks;
+ keccak_state state;
+
+ shake256_absorb_once(&state, in, inlen);
+ nblocks = outlen / SHAKE256_RATE;
+ shake256_squeezeblocks(out, nblocks, &state);
+ outlen -= nblocks * SHAKE256_RATE;
+ out += nblocks * SHAKE256_RATE;
+ shake256_squeeze(out, outlen, &state);
+}
+
+/*************************************************
+ * Name: sha3_256
+ *
+ * Description: SHA3-256 with non-incremental API
+ *
+ * Arguments: - uint8_t *h: pointer to output (32 bytes)
+ * - const uint8_t *in: pointer to input
+ * - size_t inlen: length of input in bytes
+ **************************************************/
+void sha3_256(uint8_t h[SHA3_256_HASHBYTES], const uint8_t *in, size_t inlen)
+{
+ unsigned int i;
+ uint64_t s[MLD_KECCAK_LANES];
+
+ keccak_absorb_once(s, SHA3_256_RATE, in, inlen, 0x06);
+ mld_keccakf1600_permute(s);
+ for (i = 0; i < 4; i++)
+ {
+ store64(h + 8 * i, s[i]);
+ }
+}
+
+/*************************************************
+ * Name: sha3_512
+ *
+ * Description: SHA3-512 with non-incremental API
+ *
+ * Arguments: - uint8_t *h: pointer to output (64 bytes)
+ * - const uint8_t *in: pointer to input
+ * - size_t inlen: length of input in bytes
+ **************************************************/
+void sha3_512(uint8_t h[SHA3_512_HASHBYTES], const uint8_t *in, size_t inlen)
+{
+ unsigned int i;
+ uint64_t s[MLD_KECCAK_LANES];
+
+ keccak_absorb_once(s, SHA3_512_RATE, in, inlen, 0x06);
+ mld_keccakf1600_permute(s);
+ for (i = 0; i < 8; i++)
+ {
+ store64(h + 8 * i, s[i]);
+ }
+}
+
+
+static void mld_keccak_absorb_once_x4(uint64_t *s, uint32_t r,
+ const uint8_t *in0, const uint8_t *in1,
+ const uint8_t *in2, const uint8_t *in3,
+ size_t inlen, uint8_t p)
+{
+ while (inlen >= r)
+ {
+ mld_keccakf1600x4_xor_bytes(s, in0, in1, in2, in3, 0, r);
+ mld_keccakf1600x4_permute(s);
+
+ in0 += r;
+ in1 += r;
+ in2 += r;
+ in3 += r;
+ inlen -= r;
+ }
+
+ if (inlen > 0)
+ {
+ mld_keccakf1600x4_xor_bytes(s, in0, in1, in2, in3, 0, inlen);
+ }
+
+ if (inlen == r - 1)
+ {
+ p |= 128;
+ mld_keccakf1600x4_xor_bytes(s, &p, &p, &p, &p, inlen, 1);
+ }
+ else
+ {
+ mld_keccakf1600x4_xor_bytes(s, &p, &p, &p, &p, inlen, 1);
+ p = 128;
+ mld_keccakf1600x4_xor_bytes(s, &p, &p, &p, &p, r - 1, 1);
+ }
+}
+
+static void mld_keccak_squeezeblocks_x4(uint8_t *out0, uint8_t *out1,
+ uint8_t *out2, uint8_t *out3,
+ size_t nblocks, uint64_t *s, uint32_t r)
+{
+ while (nblocks > 0)
+ {
+ mld_keccakf1600x4_permute(s);
+ mld_keccakf1600x4_extract_bytes(s, out0, out1, out2, out3, 0, r);
+
+ out0 += r;
+ out1 += r;
+ out2 += r;
+ out3 += r;
+ nblocks--;
+ }
+}
+
+void mld_shake128x4_absorb_once(mld_shake128x4ctx *state, const uint8_t *in0,
+ const uint8_t *in1, const uint8_t *in2,
+ const uint8_t *in3, size_t inlen)
+{
+ memset(state, 0, sizeof(mld_shake128x4ctx));
+ mld_keccak_absorb_once_x4(state->ctx, SHAKE128_RATE, in0, in1, in2, in3,
+ inlen, 0x1F);
+}
+
+void mld_shake128x4_squeezeblocks(uint8_t *out0, uint8_t *out1, uint8_t *out2,
+ uint8_t *out3, size_t nblocks,
+ mld_shake128x4ctx *state)
+{
+ mld_keccak_squeezeblocks_x4(out0, out1, out2, out3, nblocks, state->ctx,
+ SHAKE128_RATE);
+}
+
+void mld_shake128x4_init(mld_shake128x4ctx *state) { (void)state; }
+void mld_shake128x4_release(mld_shake128x4ctx *state)
+{
+ /* Specification: Partially implements
+ * @[FIPS203, Section 3.3, Destruction of intermediate values] */
+ (void)state;
+ /*mld_zeroize(state, sizeof(mld_shake128x4ctx));*/
+}
+
+
+void mld_shake256x4_absorb_once(mld_shake256x4ctx *state, const uint8_t *in0,
+ const uint8_t *in1, const uint8_t *in2,
+ const uint8_t *in3, size_t inlen)
+{
+ memset(state, 0, sizeof(mld_shake256x4ctx));
+ mld_keccak_absorb_once_x4(state->ctx, SHAKE256_RATE, in0, in1, in2, in3,
+ inlen, 0x1F);
+}
+
+void mld_shake256x4_squeezeblocks(uint8_t *out0, uint8_t *out1, uint8_t *out2,
+ uint8_t *out3, size_t nblocks,
+ mld_shake256x4ctx *state)
+{
+ mld_keccak_squeezeblocks_x4(out0, out1, out2, out3, nblocks, state->ctx,
+ SHAKE256_RATE);
+}
+
+void mld_shake256x4_init(mld_shake256x4ctx *state) { (void)state; }
+void mld_shake256x4_release(mld_shake256x4ctx *state)
+{
+ /* Specification: Partially implements
+ * @[FIPS203, Section 3.3, Destruction of intermediate values] */
+ (void)state;
+ /*mld_zeroize(state, sizeof(mld_shake256x4ctx));*/
+}